package com.tvunetworks.center.user.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tvunetworks.center.common.exception.ServerException;
import com.tvunetworks.center.common.model.LoginUser;
import com.tvunetworks.center.common.model.MailTemplateSendParam;
import com.tvunetworks.center.common.model.dto.api.instance.ApiCreateInstanceParam;
import com.tvunetworks.center.common.model.param.BasePageParam;
import com.tvunetworks.center.common.model.param.ReleasePidParam;
import com.tvunetworks.center.common.model.param.RequestPidParam;
import com.tvunetworks.center.common.util.*;
import com.tvunetworks.center.user.mapper.UserInstanceMapper;
import com.tvunetworks.center.user.model.UpdateInstanceParam;
import com.tvunetworks.center.user.model.UserInstance;
import com.tvunetworks.center.user.model.param.InstanceIdWithPeerId;
import com.tvunetworks.center.user.model.param.InstanceParam;
import com.tvunetworks.center.user.model.param.PageUserInstanceParam;
import com.tvunetworks.center.user.model.param.api.PageRunningInstanceIdsParam;
import com.tvunetworks.center.user.model.result.ResultConstant;
import com.tvunetworks.center.user.model.vo.InstanceMessageVo;
import com.tvunetworks.center.user.model.vo.InstanceVo;
import com.tvunetworks.center.user.model.vo.UserInstanceVo;
import com.tvunetworks.center.user.model.vo.api.PageRunningInstanceIdsVo;
import com.tvunetworks.center.user.service.InstancePeerIdService;
import com.tvunetworks.center.user.service.UserInstanceService;
import com.tvunetworks.center.user.service.feign.EmailFeignService;
import com.tvunetworks.center.user.service.feign.NewTpcFeignService;
import com.tvunetworks.center.user.util.InstanceApiUtil;
import com.tvunetworks.center.user.util.UserThreadPool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;

/**
 * @program: tvucc-aggregator
 * @description: UserInstanceServiceImpl
 * @author lebronchen
 * @create: 2020-04-23 13:57
 **/
@Service
@Slf4j
public class UseInstanceServiceImpl extends BaseService<UserInstance> implements UserInstanceService {

    @Autowired
    private InstanceApiUtil instanceApiUtil;
    @Autowired
    private NewTpcFeignService newTpcFeignService;
    @Autowired
    private InstancePeerIdService instancePeerIdService;
    @Autowired
    private UserInstanceMapper userInstanceMapper;
    @Autowired
    private EmailFeignService emailFeignService;

    @Value("${instance.cloudR.imageType}")
    private String cloudRImageType;
    @Value("${instance.cloudR.instanceType}")
    private String cloudRInstanceType;
    @Value("${instance.cloudR.createType}")
    private String createInstanceType;
    @Value("${instance.cloudR.allowUserRole}")
    private String allowUserRole;
    @Value("${instance.cloudR.appId}")
    private int cloudRAppId;
    @Value("${instance.cloudR.requestPeerIdRemark}")
    private String requestPeerIdRemark;
    @Value("${instance.cloudR.requestPeerIdNum}")
    private int requestPeerIdNum;
    @Value("${instance.cloudR.shell.multiR.peerIdSplit}")
    private String multiRPeerIdSplit;
    @Value("${instance.cloudR.origin}")
    private String cloudROrigin;
    @Value("${instance.cloudR.shell.cloudRType}")
    private String cloudRType;
    @Value("${instance.cloudR.protectNotShutdownTime}")
    private long protectNotShutdownTime;
    @Value("${instance.cloudR.email.subject}")
    private String cloudREmailSubject;
    @Value("${instance.cloudR.email.templateId}")
    private String cloudREmailTemplateId;
    @Value("${instance.cloudR.email.tableHead}")
    private String cloudREmailTableHead;
    @Value("${instance.cloudR.email.message}")
    private String cloudREmailMessage;
    @Value("${instance.cloudR.email.debugEmail}")
    private String cloudREmailDebugEmail;
    @Value("${instance.cloudR.multiR.instanceType}")
    private String cloudRMultiRInstanceType;
    @Value("${instance.cloudR.multiImageType}")
    private String cloudRMultiRImageType;
    @Value("${instance.cloudR.domainPrefix}")
    private String domainPrefix;
    @Value("${instance.cloudR.domainSuffix:T1279}")
    private String domainSuffix;
    @Value("${aliyun.domain.split:_}")
    private String aliyunDomainSplit;

    @Override
    public List listActiveRegion(LoginUser loginUser, String imageType) throws IOException {
        // 检查用户是否有权限
        checkRole(loginUser);
        imageType = StringUtils.isBlank(imageType) ? cloudRImageType : imageType;
        // 查询可用 region
        return instanceApiUtil.listActiveRegion(imageType);
    }

    @Override
    @Transactional
    public UserInstance createInstance(String session, LoginUser loginUser, InstanceParam instanceParam) throws IOException {
        String taskId = MyUtil.getUUID();
        log.error("createInstance start taskId:[{}] param:[{}] email:[{}]", taskId, instanceParam, loginUser.getEmail());
        // 格式化参数
        createInstanceFormatParam(instanceParam);

        // 检查用户是否有权限
        checkRole(loginUser);

        // 调用 TPC 获取到 peerIds
        List<String> peerIds = this.requestPidFromTPC(loginUser.getId(), session, instanceParam.getNum());
        Collections.sort(peerIds);
        log.error("createInstance call tpc get peerIds taskId:[{}] param:[{}] email:[{}] peerIds:[{}]", taskId, instanceParam, loginUser.getEmail(), peerIds);
        // instance-manage 开机
        String imageType = StringUtils.isBlank(instanceParam.getImageType()) ? cloudRImageType : instanceParam.getImageType();
        InstanceVo instance = this.createCloudRInstance(imageType, instanceParam.getRegion(), peerIds, instanceParam.getInstanceType(), loginUser.getId());
        log.error("createInstance call instance create instance taskId:[{}] param:[{}] email:[{}] instance:[{}] instance:[{}]", taskId, instanceParam, loginUser.getEmail(), peerIds, instance);
        // 检查是否开机成功
        this.checkInstanceCreated(instance, peerIds);
        log.error("createInstance checkInstanceCreated taskId:[{}] param:[{}] email:[{}] instance:[{}] instance:[{}]", taskId, instanceParam, loginUser.getEmail(), peerIds, instance);
        // TPC 绑定 instanceId 和 peerId 的关系
        boolean peerIdBindInstanceSuccess = this.tpcBindInstanceWithPeerId(instance.getInstanceId(), peerIds);
        log.error("createInstance call tpc bind instance with device taskId:[{}] param:[{}] email:[{}] instance:[{}] instance:[{}] result:[{}]", taskId, instanceParam, loginUser.getEmail(), peerIds, instance, peerIdBindInstanceSuccess);

        // 创建 UserInstance 和 InstancePeerId 记录
        UserInstance userInstance = this.createUserInstance(instance, loginUser.getId(), peerIds, peerIdBindInstanceSuccess, instanceParam.getRemark(), instanceParam.getScheduleShutdownTime());
        log.error("createInstance add user instance taskId:[{}] param:[{}] email:[{}] instance:[{}] instance:[{}] result:[{}]", taskId, instanceParam, loginUser.getEmail(), peerIds, instance, peerIdBindInstanceSuccess);

        return userInstance;
    }

    private void createInstanceFormatParam(InstanceParam instanceParam) {
        int num = instanceParam.getNum();
        if (num < 1) {
            num = 1;
        }
        instanceParam.setNum(num);

//        Long scheduleShutdownTime = instanceParam.getScheduleShutdownTime();
//        if (scheduleShutdownTime == null) {
//            throw new ServerException(ResultConstant.UserInstance.SCHEDULE_TIME_CAN_NOT_BE_NULL);
//        }
//        if (scheduleShutdownTime < System.currentTimeMillis()) {
//            throw new ServerException(ResultConstant.UserInstance.SCHEDULE_TIME_CAN_NOT_LESS_THAN_NOW);
//        }
    }

    @Override
    public void shutdownInstance(LoginUser loginUser, InstanceParam instanceParam) throws IOException {
        if (loginUser != null) {
            // 检查用户是否有权限
            checkRole(loginUser);
        }


        // 检查 UserInstance 是否存在，是否正在运行
        UserInstance userInstance = this.shutdownInstanceCheckInstanceId(instanceParam.getId());

        // 关机
        String instanceId = userInstance.getInstanceId();
        instanceApiUtil.shutdownInstance(instanceId);

        // TPC 释放 peerIds
        boolean peerIdReleaseBindInstanceSuccess = this.peerIdReleaseBindInstance(userInstance.getId(), instanceId);

        // 修改 UserInstance 状态
        int tpcBindStep = peerIdReleaseBindInstanceSuccess ? UserInstance.STEP_RELEASE_PEERID_SUCCESS : UserInstance.STEP_RELEASE_PEERID_FAILED;
        userInstance.setStatus(UserInstance.STATUS_CLOSED_BY_USER)
                .setTpcBindStep(tpcBindStep)
                .setShutdownTime(System.currentTimeMillis());
        if (loginUser != null) {
            userInstance.setShutdownUserId(loginUser.getId());
        }
        super.updateSelective(userInstance);
    }

    @Override
    public void updateInstance(UpdateInstanceParam param) {
        // 检查 UserInstance 是否存在，是否正在运行
        UserInstance userInstance = this.shutdownInstanceCheckInstanceId(param.getId());

        userInstance.setRemark(param.getRemark());

        Long scheduleShutdownTime = param.getScheduleShutdownTime();
        if (scheduleShutdownTime < System.currentTimeMillis()) {
            throw new ServerException(ResultConstant.UserInstance.SCHEDULE_TIME_CAN_NOT_LESS_THAN_NOW);
        }

        userInstance.setScheduleShutdownTime(scheduleShutdownTime);

        super.updateSelective(userInstance);
    }


    @Override
    public UserInstance getById(String id) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }
        return super.selectByPrimaryKey(id);
    }

    @Override
    public UserInstance getByInstanceId(String instanceId) {
        if (StringUtils.isEmpty(instanceId)) {
            return null;
        }
        UserInstance record = new UserInstance()
                .setInstanceId(instanceId);
        return super.selectExistOne(record);
    }

    @Override
    public PageInfo<UserInstanceVo> pageUserInstance(PageUserInstanceParam param) {
        if (param.getStatus() == null || param.getStatus().length == 0) {
            return new PageInfo<>(new ArrayList<>());
        }

        PageHelper.startPage(param.getPageNum(), param.getPageSize());

        List<UserInstanceVo> list = this.listUserInstance(param);
        if (!CollectionUtils.isEmpty(list)) {
            for (UserInstanceVo vo : list) {
                InstanceIdWithPeerId par = new InstanceIdWithPeerId()
                        .setInstanceId(vo.getInstanceId())
                        .setPeerId(vo.getPeerId());

                try {
                    String webRUrl = instancePeerIdService.getWebRUrl(par);
                    vo.setWebRUrl(webRUrl);
                } catch (Exception e) {

                }
            }
        }
        return new PageInfo<>(list);
    }

    private List<UserInstanceVo> listUserInstance(PageUserInstanceParam param){
        List<UserInstanceVo> list = userInstanceMapper.listUserInstance(param);
        return list;
    }

    @Override
    @Transactional
    public void remedyUserInstancePeerId() {
        remedyBindInstanceWithPeerId();
        remedyReleasePeerId();
    }

    @Override
    public void syncInstanceStatus() throws IOException {

        Map<String, String> map = this.multiImageType();
        Collection<String> imageTypes = map.values();
        if (CollectionUtils.isEmpty(imageTypes)) {
            return;
        }
        List<String> runningInstanceIds = new ArrayList<>();
        for (String imageType : imageTypes) {
            // 正在运行的 instance
            List<String> running = instanceApiUtil.listRunningInstance(imageType, cloudROrigin);
            if (!CollectionUtils.isEmpty(running)) {
                runningInstanceIds.addAll(running);
            }
        }
        // 查询 UserInstance 表中 running 的 instanceId
        List<String> dbRunningInstanceIds = userInstanceMapper.listRunningInstanceId();

        if (CollectionUtils.isEmpty(runningInstanceIds) && CollectionUtils.isEmpty(dbRunningInstanceIds)) {
            return;
        }

        List<String> temp = new ArrayList<>(runningInstanceIds);

        // instance-manage 服务 Running 但是 UserInstance 不是 Running 的机器，需要调用 instance-manage 关机
        runningInstanceIds.removeAll(dbRunningInstanceIds);
        // UserInstance 服务 Running 但是 instance-manage 不是 Running 的机器，需要调用修改 UserInstance 状态，并且调用 TPC 释放 peerIds
        dbRunningInstanceIds.removeAll(temp);

        if (!CollectionUtils.isEmpty(runningInstanceIds)) {
            shutdownInstances(runningInstanceIds);
        }
        if (!CollectionUtils.isEmpty(dbRunningInstanceIds)) {
            releaseClosedPeerIds(dbRunningInstanceIds);
        }

    }

    @Override
    public void sendRunningInstanceEmailToSupport() {
        PageUserInstanceParam param = new PageUserInstanceParam()
                .setStatus(new int[]{UserInstance.STATUS_RUNNING});
        param.setPageNum(1);
        param.setPageSize(BasePageParam.MAX_PAGE_SIZE);
        // 查询所有 Running 的机器
        List<UserInstanceVo> list = this.listUserInstance(param);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        // 把所有开机的机器根据 createUser 分组  email -> userInstances
        Map<String, List<UserInstanceVo>> map = new HashMap<>();
        for (UserInstanceVo vo : list) {
            String email = vo.getCreateUserEmail();
            List<UserInstanceVo> ll = map.get(email);
            if (ll == null) {
                ll = new ArrayList<>();
            }
            ll.add(vo);
            map.put(email, ll);
        }
        for (Map.Entry<String, List<UserInstanceVo>> entry : map.entrySet()) {
            String email = entry.getKey();
            List<UserInstanceVo> instances = entry.getValue();
            // 发送邮件
            UserThreadPool.SERVICETHREADPOOL.execute( () -> sendEmail(email, instances) );
        }
    }

    @Override
    public Map<String, String> multiRNum() throws IOException {
        return JsonUtil.toObject(cloudRMultiRInstanceType, Map.class);
    }

    @Override
    public Map<String, String> multiImageType() throws IOException {
        return JsonUtil.toObject(cloudRMultiRImageType, Map.class);
    }

    @Override
    public void shutdownSchedule() {
        List<String> ids = userInstanceMapper.listNeedShutdownInstancePrimaryKey(System.currentTimeMillis());
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        for (String id : ids) {
            InstanceParam instanceParam = new InstanceParam();
            instanceParam.setId(id);
            try {
                this.shutdownInstance(null, instanceParam);
            } catch (Exception e) {
                log.error(e.toString(), e);
            }
        }
    }

    private void sendEmail(String email, List<UserInstanceVo> list){

        // 封装邮件内容
        Map<String, Object> params = new HashMap<>();
        params.put("message", cloudREmailMessage);

        Map<String, Object> table = new HashMap<>();
        // 封装 Table Head
        List<String> headerList = Arrays.asList(cloudREmailTableHead.split(","));
        table.put("head", headerList);
        // 封装 Table Data
        List<Object> tds = new ArrayList<>();
        for (UserInstanceVo vo : list) {
            Map<String, Object> td = vo.convertToEmailParam();
            tds.add(td);
        }
        table.put("data", tds);
        params.put("table", table);

        // 发送邮件参数
        MailTemplateSendParam templateSendParam = new MailTemplateSendParam();
        if (StringUtils.isBlank(cloudREmailDebugEmail)) {
            templateSendParam.setEmail(email);
        } else {
            templateSendParam.setEmail(cloudREmailDebugEmail);
        }
        templateSendParam.setMailTemplateId(cloudREmailTemplateId);
        templateSendParam.setSubject(cloudREmailSubject);
        try {
            templateSendParam.setExtParams(JsonUtil.toJsonStr(params));
        } catch (IOException e) {
            log.error(e.toString(), e);
        }

        try {
            Result<Object> result = emailFeignService.sendMailByMailTemplate(templateSendParam);
            if (!result.assertSuccess()) {
                log.error("Send UserInstance Email Failed param:[{}] result:[{}]", templateSendParam, result.getResult());
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }

    // instance-manage 服务 Running 但是 UserInstance 不是 Running 的机器，需要调用 instance-manage 关机
    private void shutdownInstances(List<String> instanceIds){
        if (CollectionUtils.isEmpty(instanceIds)) {
            return;
        }
        for (String instanceId : instanceIds) {
            InstanceVo instance = null;
            try {
                instance = instanceApiUtil.getInstance(instanceId);
            } catch (IOException e) {
                log.error(e.toString(), e);
            }

            if (instance == null) {
                log.error("syncInstanceStatus check instance Running in instance-manage but not running in UserInstance but desc instance failed skip it instanceId:{}", instanceId);
                continue;
            }

            long launchTime = instance.getLaunchTime().longValue();
            long now = System.currentTimeMillis();
            if (now - launchTime < protectNotShutdownTime) {
                log.error("syncInstanceStatus check instance Running in instance-manage but not running in UserInstance but instance is recently launch skip it instanceId:{} launchTime:{} now:{}", instanceId, launchTime, now);
                continue;
            }

            String status = instance.getStatus();
            if (!StringUtils.equals("running", status)) {
                log.error("syncInstanceStatus check instance Running in instance-manage but not running in UserInstance but instance current status not running skip it instanceId:{} status:{}", instanceId, status);
                continue;
            }

            try {
                log.error("syncInstanceStatus need shutdown instance check instance Running in instance-manage but not running in UserInstance instanceId:{}", instanceId);
                instanceApiUtil.shutdownInstance(instanceId);
            } catch (IOException e) {
                log.error(e.toString(), e);
            }
        }
    }

    // UserInstance 服务 Running 但是 instance-manage 不是 Running 的机器，需要调用修改 UserInstance 状态，并且调用 TPC 释放 peerIds
    private void releaseClosedPeerIds(List<String> instanceIds) {
        if (CollectionUtils.isEmpty(instanceIds)) {
            return;
        }
        List<UserInstance> list = userInstanceMapper.listByInstanceIds(instanceIds);
        if (CollectionUtils.isEmpty(list)) {
            log.error("releaseClosedPeerIds failed listByInstanceIds return null instanceIds:{}", instanceIds);
            return;
        }
        for (UserInstance userInstance : list) {

            boolean releaseSuccess = this.peerIdReleaseBindInstance(userInstance.getId(), userInstance.getInstanceId());
            // 修改 UserInstance 状态，并调用 TPC 释放 peerIds
            int tpcBindStep = releaseSuccess ? UserInstance.STEP_RELEASE_PEERID_SUCCESS : UserInstance.STEP_RELEASE_PEERID_FAILED;
            userInstance.setStatus(UserInstance.STATUS_CLOSED_BY_SYSTEM)
                    .setTpcBindStep(tpcBindStep)
                    .setShutdownTime(System.currentTimeMillis());
            super.updateSelective(userInstance);
        }
    }

    /**
     * 补救获取 PID 后，绑定 instanceId 和 peerIds 失败情况
     */
    private void remedyBindInstanceWithPeerId(){
        List<UserInstance> list = userInstanceMapper.listNeedBindRecord();
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (UserInstance userInstance : list) {
            List<String> peerIds = instancePeerIdService.listPeerIdByUserInstanceId(userInstance.getId());
            if (CollectionUtils.isEmpty(peerIds)) {
                log.error("remedyBindInstanceWithPeerId failed listPeerIdByUserInstanceId return null userInstance:{}", userInstance);
                continue;
            }
            log.error("remedyBindInstanceWithPeerId userInstance:{} instanceId:{} peerIds:{}", userInstance, userInstance.getInstanceId(), peerIds);
            boolean bindSuccess = this.tpcBindInstanceWithPeerId(userInstance.getInstanceId(), peerIds);
            if (bindSuccess) {
                userInstance.setTpcBindStep(UserInstance.STEP_PEERID_BIND_INSTANCE_SUCCESS);
                super.update(userInstance);
            }
        }
    }

    /**
     * 补救关机后， peerIds 释放失败情况
     */
    private void remedyReleasePeerId(){
        List<UserInstance> list = userInstanceMapper.listNeedReleaseRecord();
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (UserInstance userInstance : list) {
            List<String> peerIds = instancePeerIdService.listPeerIdByUserInstanceId(userInstance.getId());
            if (CollectionUtils.isEmpty(peerIds)) {
                log.error("remedyBindInstanceWithPeerId failed listPeerIdByUserInstanceId return null userInstance:{}", userInstance);
                continue;
            }
            log.error("remedyBindInstanceWithPeerId userInstance:{} instanceId:{} peerIds:{}", userInstance, userInstance.getInstanceId(), peerIds);
            boolean releaseSuccess = this.peerIdReleaseBindInstance(userInstance.getId(), userInstance.getInstanceId());
            if (releaseSuccess) {
                userInstance.setTpcBindStep(UserInstance.STEP_RELEASE_PEERID_SUCCESS);
                super.update(userInstance);
            }
        }
    }

    /**
     * 调用 TPC 接口获取 peerIds
     * @param userId
     * @param session
     * @return
     */
    private List<String> requestPidFromTPC(String userId, String session, int num){
        RequestPidParam param = new RequestPidParam()
                .setAppId((byte) cloudRAppId)
                .setUserId(userId)
                .setCount(num)
                .setSession(session)
                .setRemark(requestPeerIdRemark);
        Result<List<String>> requestPidRequest = newTpcFeignService.requestPid(param);
        if (!requestPidRequest.assertSuccess()) {
            // 调用 TPC 获取 PID 失败
            log.error("UseInstanceServiceImpl requestPidFromTPC from TPC failed param:{} result:{}", param, requestPidRequest);
            ServerException.Exception(ResultConstant.TPC.REQUEST_PID_FAILED);
        }

        // 获取到 peerIds，并生成 shell 脚本占位符
        List<String> peerIds = requestPidRequest.getResult();
        if (CollectionUtils.isEmpty(peerIds)) {
            // 调用 TPC 没有获取到 PID
            log.error("UseInstanceServiceImpl requestPidFromTPC from TPC return null peerIds param:{} result:{}", param, requestPidRequest);
            ServerException.Exception(ResultConstant.TPC.REQUEST_NULL_PID);
        }

        log.error("UseInstanceServiceImpl requestPidFromTPC from TPC Success peerIds param:{} peerIds:{}", param, peerIds);
        return peerIds;
    }

    /**
     * 调用 Instance 开机
     *
     * @param region
     * @param peerIds
     * @return
     * @throws IOException
     */
    private InstanceVo createCloudRInstance(String imageType, String region, List<String> peerIds, String instanceType, String userId) throws IOException {
        if (StringUtils.isBlank(instanceType)) {
            // 配置文件中，虚拟机 R 数量对应的 实例类型
            Map<String, String> multiRInstanceType = JsonUtil.toObject(cloudRMultiRInstanceType, Map.class);
            instanceType = multiRInstanceType.get(String.valueOf(peerIds.size()));
        }

        // 生成 shell 脚本占位符
        String shellPlaceholder = this.packageShellPlaceholder(peerIds);
        // 开机
        ApiCreateInstanceParam param = new ApiCreateInstanceParam();
        param.setType(createInstanceType);
        param.setImageType(imageType);
        param.setRegion(region);
        param.setOrigin(cloudROrigin);
        param.setShellPlaceholder(shellPlaceholder);
        param.setUserId(userId);
        if (StringUtils.isNotEmpty(cloudRInstanceType)) {
            param.setInstanceType(cloudRInstanceType);
        }
        if (StringUtils.isNotBlank(instanceType)) {
            log.error("UseInstanceServiceImpl createCloudRInstance R num:{} instanceType:{} param:{}", peerIds.size(), instanceType, param);
            param.setInstanceType(instanceType);
        }
        List<String> domains = new ArrayList<>();
        for (String peerId : peerIds) {
            //domain用MD5加密的模式生成
            if (StringUtils.isNotBlank(domainPrefix)) {
                domains.add(EndecryptUtil.MD5(domainPrefix + aliyunDomainSplit + peerId.toUpperCase() + domainSuffix));
            } else {
                domains.add(EndecryptUtil.MD5(peerId.toUpperCase() + domainSuffix));
            }
        }
        param.setBindInstanceDomainPeerIds(domains);
//        param.setInstanceDomainPrefix(domainPrefix);
        InstanceVo instance = instanceApiUtil.createInstance(param);
        log.error("UseInstanceServiceImpl createCloudRInstance from instance-manage Success peerIds param:{} instance:{}", param, instance);
        return instance;
    }

    /**
     * 生成开机 shell 脚本占位符
     * @param peerIds
     * @return
     */
    private String packageShellPlaceholder(List<String> peerIds){
        StringBuilder shellPlaceholder = new StringBuilder(cloudRType).append(",");
        int size = peerIds.size();
        for (int i = 0; i < size; i++) {
            shellPlaceholder.append(peerIds.get(i).toUpperCase());
            if (i + 1 < size) {
                shellPlaceholder.append(multiRPeerIdSplit);
            }
        }
        return shellPlaceholder.toString();
    }

    /**
     * 检查是否开机成功，如果开机失败，需要调用 TPC 释放掉 pid
     * @param instance
     * @param peerIds
     */
    private void checkInstanceCreated(InstanceVo instance, List<String> peerIds) {
        if (instance == null || StringUtils.isBlank(instance.getInstanceId())) {
            ReleasePidParam releasePidParam = new ReleasePidParam()
                    .setPids(peerIds);
            newTpcFeignService.releasePid(releasePidParam);
            log.error("UseInstanceServiceImpl checkInstanceCreated failed created instance is null release peerIds:{}", peerIds);
            ServerException.Exception(ResultConstant.Instance.CREATED_INSTANCE_IS_NULL);
        }
    }

    /**
     * 调用 TPC 将 instanceId 和 peerIds 绑定
     * @param instanceId
     * @param peerIds
     * @return
     */
    private boolean tpcBindInstanceWithPeerId(String instanceId, List<String> peerIds){
        boolean peerIdBindInstanceSuccess = false;
        try {
            Result<Object> bindResult = newTpcFeignService.addInstanceIdByPid(instanceId, peerIds);
            if (bindResult.assertSuccess()) {
                peerIdBindInstanceSuccess = true;
                log.error("UseInstanceServiceImpl tpcBindInstanceWithPeerId success instanceId:{} peerIds:{} result:{}", instanceId, peerIds, bindResult);
            } else {
                log.error("UseInstanceServiceImpl tpcBindInstanceWithPeerId failed instanceId:{} peerIds:{} result:{}", instanceId, peerIds, bindResult);
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
        return peerIdBindInstanceSuccess;
    }

    /**
     * 创建完实例后，插入 UserInstance 和 InstancePeerId
     * @param instance
     * @param userId
     * @param peerIds
     * @param peerIdBindInstanceSuccess
     */
    private UserInstance createUserInstance(InstanceVo instance, String userId, List<String> peerIds, boolean peerIdBindInstanceSuccess, String remark, Long scheduleShutdownTime){
        // 创建 UserInstance
        int tpcBindStep = peerIdBindInstanceSuccess ? UserInstance.STEP_PEERID_BIND_INSTANCE_SUCCESS : UserInstance.STEP_PEERID_NOT_BIND_INSTANCE;
        UserInstance record = new UserInstance()
                .setUserId(userId)
                .setStatus(UserInstance.STATUS_RUNNING)
                .setType(UserInstance.TYPE_SUPPORT_CLOUD_R)
                .setTpcBindStep(tpcBindStep)
                .setImageType(instance.getImageType())
                .setRemark(remark)
                .setScheduleShutdownTime(scheduleShutdownTime);
        BeanUtils.copyProperties(instance, record);
        super.insert(record);
        log.error("UseInstanceServiceImpl createUserInstance success userId:{} instance:{}", userId, instance);

        // 创建 InstancePeerId
        instancePeerIdService.addInstancePeerId(record.getId(), peerIds);
        log.error("UseInstanceServiceImpl addInstancePeerId success userInstanceId:{} peerIds:{}", record.getId(), peerIds);
        return record;
    }

    /**
     * ShutdownInstance 接口检查参数
     * @param userInstanceId
     * @return
     */
    private UserInstance shutdownInstanceCheckInstanceId(String userInstanceId){
        UserInstance userInstance = super.selectExistByPrimaryKey(userInstanceId);
        if (userInstance == null) {
            log.error("UseInstanceServiceImpl shutdownInstance failed instance not exist userInstanceId:{}", userInstanceId);
            ServerException.Exception(ResultConstant.UserInstance.NOT_EXIST);
        }
        if (userInstance.getStatus() == null || userInstance.getStatus() != UserInstance.STATUS_RUNNING) {
            log.error("UseInstanceServiceImpl shutdownInstance failed instance already closed userInstanceId:{}", userInstanceId);
            ServerException.Exception(ResultConstant.UserInstance.ALREADY_CLOSED);
        }
        return userInstance;
    }

    /**
     * 调用 TPC 释放 instance 和 peerIds 的关系
     * @param userInstanceId
     * @return
     */
    private boolean peerIdReleaseBindInstance(String userInstanceId, String instanceId) {
        List<String> peerIds = instancePeerIdService.listPeerIdByUserInstanceId(userInstanceId);
        if (CollectionUtils.isEmpty(peerIds)) {
            log.error("UseInstanceServiceImpl peerIdReleaseBindInstance failed, listPeerIdByUserInstanceId return null userInstanceId:{} instanceId:{}", userInstanceId, instanceId);
            return true;
        }

        boolean peerIdReleaseBindInstanceSuccess = false;
        try {
            ReleasePidParam releasePidParam = new ReleasePidParam()
                    .setPids(peerIds);
            Result<Boolean> releaseResult = newTpcFeignService.releasePid(releasePidParam);
            if (releaseResult.assertSuccess()) {
                peerIdReleaseBindInstanceSuccess = true;
                log.error("UseInstanceServiceImpl peerIdReleaseBindInstance call TPC releasePid success peerIds:{} result:{}", peerIds, releaseResult);
            } else {
                log.error("UseInstanceServiceImpl peerIdReleaseBindInstance call TPC releasePid failed peerIds:{} result:{}", peerIds, releaseResult);
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
        log.error("UseInstanceServiceImpl peerIdReleaseBindInstance call TPC releasePid success peerIds:{}", peerIds);
        return peerIdReleaseBindInstanceSuccess;
    }

    /**
     * 检查用户是否有权限
     * @param loginUser
     */
    private void checkRole(LoginUser loginUser) {
        boolean allow = false;
        String role = loginUser.getRole();
        String[] roleArray = allowUserRole.split(",");
        for (String r : roleArray) {
            if (StringUtils.equals(r, role)) {
                allow = true;
                break;
            }
        }
        if (!allow) {
            ServerException.Exception(ResultUtil.noAuthority());
        }

    }

    @Override
    public PageRunningInstanceIdsVo<String> pageRunningInstanceIds(PageRunningInstanceIdsParam param) {
        PageHelper.startPage(param.getPageNum(), param.getPageSize());
        if (StringUtils.isEmpty(param.getProjectType())) {
            return new PageRunningInstanceIdsVo<>();
        }
        List<String> imageTypes = new ArrayList<>(Arrays.asList(param.getProjectType().split(",")));
        List<String> instanceIds = userInstanceMapper.pageRunningInstance(imageTypes);
        PageInfo<String> stringPageInfo = new PageInfo<>(instanceIds);
        return new PageRunningInstanceIdsVo<String>()
                .setList(instanceIds)
                .setPages(stringPageInfo.getPages())
                .setTotal(stringPageInfo.getTotal());
    }

    @Override
    public List<InstanceMessageVo> getInstanceMessage(List<String> instanceIds) {
        if (CollectionUtils.isEmpty(instanceIds)) {
            return new ArrayList<>();
        }
        return userInstanceMapper.getInstanceMessage(instanceIds);
    }
}
